﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using SharedGenomics.TextMining;

namespace SharedGenomics.Workbench.Tests.TextMining
{
    [TestClass]
    public class StringTilerTests
    {
        private ScoredTermCollection col;

        [TestInitialize]
        public void Setup()
        {
            this.col = new ScoredTermCollection();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ConstructorTestInvalidNull()
        {
            StringTiler st = new StringTiler(null);
        }

        [TestMethod]
        public void TileTestTextContainsNoTerms()
        {
            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("terms collection is empty");
            var tileArray = tiles.ToArray();
            Assert.AreEqual(1, tileArray.Length);
            Assert.AreEqual("terms collection is empty", tileArray[0].Text);

            //non-terms should have a score of zero
            Assert.AreEqual(0.0, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestSingleTermAtEnd()
        {
            //add term to collection
            this.col.Add(new ScoredTerm("fox", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("the quick brown fox");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(2, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "the quick brown ", false);
            this.AssertTileProperties(tileArray[1], "fox", true);

            Assert.AreEqual(0.1, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestSingleTermAtStart()
        {
            //add term to collection
            this.col.Add(new ScoredTerm("fox", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("fox jumps over the");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(2, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "fox", true);
            this.AssertTileProperties(tileArray[1], " jumps over the", false);

            Assert.AreEqual(0.1, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestSingleTermInMiddle()
        {
            //add term to collection
            this.col.Add(new ScoredTerm("fox", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("the quick brown fox jumps over the");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(3, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "the quick brown ", false);
            this.AssertTileProperties(tileArray[1], "fox", true);
            this.AssertTileProperties(tileArray[2], " jumps over the", false);

            Assert.AreEqual(0.1, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestMultipleUniqueTerms()
        {
            //add terms to the collection
            this.col.Add(new ScoredTerm("fox", 0.1));
            this.col.Add(new ScoredTerm("dog", 0.4));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("the quick brown fox jumps over the lazy dog");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(4, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "the quick brown ", false);
            this.AssertTileProperties(tileArray[1], "fox", true);
            this.AssertTileProperties(tileArray[2], " jumps over the lazy ", false);
            this.AssertTileProperties(tileArray[3], "dog", true);

            Assert.AreEqual(0.25, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestDuplicateTerms()
        {
            this.col.Add(new ScoredTerm("monkey", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("monkey see monkey do");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(4, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "monkey", true);
            this.AssertTileProperties(tileArray[1], " see ", false);
            this.AssertTileProperties(tileArray[2], "monkey", true);
            this.AssertTileProperties(tileArray[3], " do", false);

            Assert.AreEqual(0.1, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestTermHasNonPositiveCValue()
        {
            this.col.Add(new ScoredTerm("fox", 0.1));
            this.col.Add(new ScoredTerm("dog", -2.3));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("the quick brown fox jumped over the lazy dog");
            var tileArray = tiles.ToArray();

            //NOTE: 'dog' should NOT be included as a term since it has a negative c-value
            Assert.AreEqual(3, tileArray.Length);

            this.AssertTileProperties(tileArray[0], "the quick brown ", false);
            this.AssertTileProperties(tileArray[1], "fox", true);
            this.AssertTileProperties(tileArray[2], " jumped over the lazy dog", false);

            Assert.AreEqual(0.1, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestTermCollectionContainsSubTerms()
        {
            this.col.Add(new ScoredTerm("fox jumped", 0.4));
            this.col.Add(new ScoredTerm("fox", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tiles = tiler.Tile("the quick brown fox jumped");
            var tileArray = tiles.ToArray();

            Assert.AreEqual(2, tileArray.Length);
            this.AssertTileProperties(tileArray[0], "the quick brown ", false);
            this.AssertTileProperties(tileArray[1], "fox jumped", true);

            Assert.AreEqual(0.4, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestCaseInsensitiveTermMatch()
        {
            this.col.Add(new ScoredTerm("fox", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tileArray = tiler.Tile("Fox jumped").ToArray();

            Assert.AreEqual(2, tileArray.Length);

            //NOTE: the tile text should match the input text, not the term text so the capital 'F' should be retained
            this.AssertTileProperties(tileArray[0], "Fox", true);
            this.AssertTileProperties(tileArray[1], " jumped", false);
        }

        [TestMethod]
        public void TileTestPartialWordMatch()
        {
            this.col.Add(new ScoredTerm("dog", 0.1));

            StringTiler tiler = new StringTiler(this.col);

            var tileArray = tiler.Tile("quick brown dogs jumped").ToArray();

            Assert.AreEqual(3, tileArray.Length);

            //the 'dog' term should be matched even though it does not match an entire word
            this.AssertTileProperties(tileArray[0], "quick brown ", false);
            this.AssertTileProperties(tileArray[1], "dog", true);
            this.AssertTileProperties(tileArray[2], "s jumped", false);
        }

        [TestMethod]
        public void TileTestNull()
        {
            StringTiler tiler = new StringTiler(this.col);
            var tiles = tiler.Tile(null);

            Assert.AreEqual(0, tiles.Count());
            Assert.AreEqual(0.0, tiles.CalculateScore());
        }

        [TestMethod]
        public void TileTestEmpty()
        {
            StringTiler tiler = new StringTiler(this.col);
            var tiles = tiler.Tile(String.Empty);

            Assert.AreEqual(0, tiles.Count());
            Assert.AreEqual(0.0, tiles.CalculateScore());
        }

        private void AssertTileProperties(TileSection tile, string text, bool isTerm)
        {
            Assert.AreEqual(text, tile.Text);
            Assert.AreEqual(isTerm, tile.IsTerm);
        }
    }
}
